package com.fh.common.util;

import java.io.*;
import java.util.ArrayList;
import java.util.List;


/**
 * @author Qiu,Junda 
 * @date 2019年4月9日
 * @description
 */
public class FileUtils {

	 public final static String FILE_EXTENSION_SEPARATOR = ".";

	    private FileUtils() {
	        throw new AssertionError();
	    }

	    /**
	     * read file
	     * 
	     * @param filePath
	     * @param charsetName The name of a supported {@link java.nio.charset.Charset </code>charset<code>}
	     * @return if file not exist, return null, else return content of file
	     * @throws RuntimeException if an error occurs while operator BufferedReader
	     */
	    public static StringBuilder readFile(String filePath, String charsetName) {
	        File file = new File(filePath);
	        StringBuilder fileContent = new StringBuilder("");
	        if (file == null || !file.isFile()) {
	            return null;
	        }

	        BufferedReader reader = null;
	        try {
	            InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
	            reader = new BufferedReader(is);
	            String line = null;
	            while ((line = reader.readLine()) != null) {
	                if (!"".equals(fileContent.toString())) {
	                    fileContent.append("\r\n");
	                }
	                fileContent.append(line);
	            }
	            return fileContent;
	        } catch (IOException e) {
	            throw new RuntimeException("IOException occurred. ", e);
	        } finally {
	            IOUtils.close(reader);
	        }
	    }

	    /**
	     * write file
	     * 
	     * @param filePath
	     * @param content
	     * @param append is append, if true, write to the end of file, else clear content of file and write into it
	     * @return return false if content is empty, true otherwise
	     * @throws RuntimeException if an error occurs while operator FileWriter
	     */
	    public static boolean writeFile(String filePath, String content, boolean append) {
	        if (StringUtils.isEmpty(content)) {
	            return false;
	        }

	        FileWriter fileWriter = null;
	        try {
	            makeDirs(filePath);
	            fileWriter = new FileWriter(filePath, append);
	            fileWriter.write(content);
	            return true;
	        } catch (IOException e) {
	            throw new RuntimeException("IOException occurred. ", e);
	        } finally {
	            IOUtils.close(fileWriter);
	        }
	    }

	    /**
	     * write file
	     * 
	     * @param filePath
	     * @param contentList
	     * @param append is append, if true, write to the end of file, else clear content of file and write into it
	     * @return return false if contentList is empty, true otherwise
	     * @throws RuntimeException if an error occurs while operator FileWriter
	     */
	    public static boolean writeFile(String filePath, List<String> contentList, boolean append) {
	        if (ListUtils.isEmpty(contentList)) {
	            return false;
	        }

	        FileWriter fileWriter = null;
	        try {
	            makeDirs(filePath);
	            fileWriter = new FileWriter(filePath, append);
	            int i = 0;
	            for (String line : contentList) {
	                if (i++ > 0) {
	                    fileWriter.write("\r\n");
	                }
	                fileWriter.write(line);
	            }
	            return true;
	        } catch (IOException e) {
	            throw new RuntimeException("IOException occurred. ", e);
	        } finally {
	            IOUtils.close(fileWriter);
	        }
	    }

	    /**
	     * write file, the string will be written to the begin of the file
	     * 
	     * @param filePath
	     * @param content
	     * @return
	     */
	    public static boolean writeFile(String filePath, String content) {
	        return writeFile(filePath, content, false);
	    }

	    /**
	     * write file, the string list will be written to the begin of the file
	     * 
	     * @param filePath
	     * @param contentList
	     * @return
	     */
	    public static boolean writeFile(String filePath, List<String> contentList) {
	        return writeFile(filePath, contentList, false);
	    }

	    /**
	     * write file, the bytes will be written to the begin of the file
	     * 
	     * @param filePath
	     * @param stream
	     * @return
	     * @see {@link #writeFile(String, InputStream, boolean)}
	     */
	    public static boolean writeFile(String filePath, InputStream stream) {
	        return writeFile(filePath, stream, false);
	    }

	    /**
	     * write file
	     * 
	     * @param file the file to be opened for writing.
	     * @param stream the input stream
	     * @param append if <code>true</code>, then bytes will be written to the end of the file rather than the beginning
	     * @return return true
	     * @throws RuntimeException if an error occurs while operator FileOutputStream
	     */
	    public static boolean writeFile(String filePath, InputStream stream, boolean append) {
	        return writeFile(filePath != null ? new File(filePath) : null, stream, append);
	    }

	    /**
	     * write file, the bytes will be written to the begin of the file
	     * 
	     * @param file
	     * @param stream
	     * @return
	     * @see {@link #writeFile(File, InputStream, boolean)}
	     */
	    public static boolean writeFile(File file, InputStream stream) {
	        return writeFile(file, stream, false);
	    }

	    /**
	     * write file
	     * 
	     * @param file the file to be opened for writing.
	     * @param stream the input stream
	     * @param append if <code>true</code>, then bytes will be written to the end of the file rather than the beginning
	     * @return return true
	     * @throws RuntimeException if an error occurs while operator FileOutputStream
	     */
	    public static boolean writeFile(File file, InputStream stream, boolean append) {
	        OutputStream o = null;
	        try {
	            makeDirs(file.getAbsolutePath());
	            o = new FileOutputStream(file, append);
	            byte data[] = new byte[1024];
	            int length = -1;
	            while ((length = stream.read(data)) != -1) {
	                o.write(data, 0, length);
	            }
	            o.flush();
	            return true;
	        } catch (FileNotFoundException e) {
	            throw new RuntimeException("FileNotFoundException occurred. ", e);
	        } catch (IOException e) {
	            throw new RuntimeException("IOException occurred. ", e);
	        } finally {
	            IOUtils.close(o);
	            IOUtils.close(stream);
	        }
	    }

	    /**
	     * move file
	     * 
	     * @param sourceFilePath
	     * @param destFilePath
	     */
	    public static void moveFile(String sourceFilePath, String destFilePath) {
	        if (StringUtils.isEmpty(sourceFilePath) || StringUtils.isEmpty(destFilePath)) {
	            throw new RuntimeException("Both sourceFilePath and destFilePath cannot be null.");
	        }
	        moveFile(new File(sourceFilePath), new File(destFilePath));
	    }

	    /**
	     * move file
	     * 
	     * @param srcFile
	     * @param destFile
	     */
	    public static void moveFile(File srcFile, File destFile) {
	        boolean rename = srcFile.renameTo(destFile);
	        if (!rename) {
	            copyFile(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
	            deleteFile(srcFile.getAbsolutePath());
	        }
	    }

	    /**
	     * copy file
	     * 
	     * @param sourceFilePath
	     * @param destFilePath
	     * @return
	     * @throws RuntimeException if an error occurs while operator FileOutputStream
	     */
	    public static boolean copyFile(String sourceFilePath, String destFilePath) {
	        InputStream inputStream = null;
	        try {
	            inputStream = new FileInputStream(sourceFilePath);
	        } catch (FileNotFoundException e) {
	            throw new RuntimeException("FileNotFoundException occurred. ", e);
	        }
	        return writeFile(destFilePath, inputStream);
	    }

	    /**
	     * read file to string list, a element of list is a line
	     * 
	     * @param filePath
	     * @param charsetName The name of a supported {@link java.nio.charset.Charset </code>charset<code>}
	     * @return if file not exist, return null, else return content of file
	     * @throws RuntimeException if an error occurs while operator BufferedReader
	     */
	    public static List<String> readFileToList(String filePath, String charsetName) {
	        File file = new File(filePath);
	        List<String> fileContent = new ArrayList<String>();
	        if (file == null || !file.isFile()) {
	            return null;
	        }

	        BufferedReader reader = null;
	        try {
	            InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
	            reader = new BufferedReader(is);
	            String line = null;
	            while ((line = reader.readLine()) != null) {
	                fileContent.add(line);
	            }
	            return fileContent;
	        } catch (IOException e) {
	            throw new RuntimeException("IOException occurred. ", e);
	        } finally {
	            IOUtils.close(reader);
	        }
	    }

	    /**
	     * get file name from path, not include suffix
	     * 
	     * <pre>
	     *      getFileNameWithoutExtension(null)               =   null
	     *      getFileNameWithoutExtension("")                 =   ""
	     *      getFileNameWithoutExtension("   ")              =   "   "
	     *      getFileNameWithoutExtension("abc")              =   "abc"
	     *      getFileNameWithoutExtension("a.mp3")            =   "a"
	     *      getFileNameWithoutExtension("a.b.rmvb")         =   "a.b"
	     *      getFileNameWithoutExtension("c:\\")              =   ""
	     *      getFileNameWithoutExtension("c:\\a")             =   "a"
	     *      getFileNameWithoutExtension("c:\\a.b")           =   "a"
	     *      getFileNameWithoutExtension("c:a.txt\\a")        =   "a"
	     *      getFileNameWithoutExtension("/home/admin")      =   "admin"
	     *      getFileNameWithoutExtension("/home/admin/a.txt/b.mp3")  =   "b"
	     * </pre>
	     * 
	     * @param filePath
	     * @return file name from path, not include suffix
	     * @see
	     */
	    public static String getFileNameWithoutExtension(String filePath) {
	        if (StringUtils.isEmpty(filePath)) {
	            return filePath;
	        }

	        int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
	        int filePosi = filePath.lastIndexOf(File.separator);
	        if (filePosi == -1) {
	            return (extenPosi == -1 ? filePath : filePath.substring(0, extenPosi));
	        }
	        if (extenPosi == -1) {
	            return filePath.substring(filePosi + 1);
	        }
	        return (filePosi < extenPosi ? filePath.substring(filePosi + 1, extenPosi) : filePath.substring(filePosi + 1));
	    }

	    /**
	     * get file name from path, include suffix
	     * 
	     * <pre>
	     *      getFileName(null)               =   null
	     *      getFileName("")                 =   ""
	     *      getFileName("   ")              =   "   "
	     *      getFileName("a.mp3")            =   "a.mp3"
	     *      getFileName("a.b.rmvb")         =   "a.b.rmvb"
	     *      getFileName("abc")              =   "abc"
	     *      getFileName("c:\\")              =   ""
	     *      getFileName("c:\\a")             =   "a"
	     *      getFileName("c:\\a.b")           =   "a.b"
	     *      getFileName("c:a.txt\\a")        =   "a"
	     *      getFileName("/home/admin")      =   "admin"
	     *      getFileName("/home/admin/a.txt/b.mp3")  =   "b.mp3"
	     * </pre>
	     * 
	     * @param filePath
	     * @return file name from path, include suffix
	     */
	    public static String getFileName(String filePath) {
	        if (StringUtils.isEmpty(filePath)) {
	            return filePath;
	        }

	        int filePosi = filePath.lastIndexOf(File.separator);
	        return (filePosi == -1) ? filePath : filePath.substring(filePosi + 1);
	    }

	    /**
	     * get folder name from path
	     * 
	     * <pre>
	     *      getFolderName(null)               =   null
	     *      getFolderName("")                 =   ""
	     *      getFolderName("   ")              =   ""
	     *      getFolderName("a.mp3")            =   ""
	     *      getFolderName("a.b.rmvb")         =   ""
	     *      getFolderName("abc")              =   ""
	     *      getFolderName("c:\\")              =   "c:"
	     *      getFolderName("c:\\a")             =   "c:"
	     *      getFolderName("c:\\a.b")           =   "c:"
	     *      getFolderName("c:a.txt\\a")        =   "c:a.txt"
	     *      getFolderName("c:a\\b\\c\\d.txt")    =   "c:a\\b\\c"
	     *      getFolderName("/home/admin")      =   "/home"
	     *      getFolderName("/home/admin/a.txt/b.mp3")  =   "/home/admin/a.txt"
	     * </pre>
	     * 
	     * @param filePath
	     * @return
	     */
	    public static String getFolderName(String filePath) {

	        if (StringUtils.isEmpty(filePath)) {
	            return filePath;
	        }

	        int filePosi = filePath.lastIndexOf(File.separator);
	        return (filePosi == -1) ? "" : filePath.substring(0, filePosi);
	    }

	    /**
	     * get suffix of file from path
	     * 
	     * <pre>
	     *      getFileExtension(null)               =   ""
	     *      getFileExtension("")                 =   ""
	     *      getFileExtension("   ")              =   "   "
	     *      getFileExtension("a.mp3")            =   "mp3"
	     *      getFileExtension("a.b.rmvb")         =   "rmvb"
	     *      getFileExtension("abc")              =   ""
	     *      getFileExtension("c:\\")              =   ""
	     *      getFileExtension("c:\\a")             =   ""
	     *      getFileExtension("c:\\a.b")           =   "b"
	     *      getFileExtension("c:a.txt\\a")        =   ""
	     *      getFileExtension("/home/admin")      =   ""
	     *      getFileExtension("/home/admin/a.txt/b")  =   ""
	     *      getFileExtension("/home/admin/a.txt/b.mp3")  =   "mp3"
	     * </pre>
	     * 
	     * @param filePath
	     * @return
	     */
	    public static String getFileExtension(String filePath) {
	        if (ConvertUtils.isEmpty(filePath)) {
	            return filePath;
	        }

	        int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
	        int filePosi = filePath.lastIndexOf(File.separator);
	        if (extenPosi == -1) {
	            return "";
	        }
	        return (filePosi >= extenPosi) ? "" : filePath.substring(extenPosi + 1);
	    }

	    /**
	     * Creates the directory named by the trailing filename of this file, including the complete directory path required
	     * to create this directory. <br/>
	     * <br/>
	     * <ul>
	     * <strong>Attentions:</strong>
	     * <li>makeDirs("C:\\Users\\Trinea") can only create users folder</li>
	     * <li>makeFolder("C:\\Users\\Trinea\\") can create Trinea folder</li>
	     * </ul>
	     * 
	     * @param filePath
	     * @return true if the necessary directories have been created or the target directory already exists, false one of
	     *         the directories can not be created.
	     *         <ul>
	     *         <li>if {@link FileUtils#getFolderName(String)} return null, return false</li>
	     *         <li>if target directory already exists, return true</li>
	     *         <li>return {@link File#makeFolder}</li>
	     *         </ul>
	     */
	    public static boolean makeDirs(String filePath) {
	        String folderName = getFolderName(filePath);
	        if (StringUtils.isEmpty(folderName)) {
	            return false;
	        }

	        File folder = new File(folderName);
	        return (folder.exists() && folder.isDirectory()) ? true : folder.mkdirs();
	    }

	    /**
	     * @param filePath
	     * @return
	     * @see #makeDirs(String)
	     */
	    public static boolean makeFolders(String filePath) {
	        return makeDirs(filePath);
	    }

	    /**
	     * Indicates if this file represents a file on the underlying file system.
	     * 
	     * @param filePath
	     * @return
	     */
	    public static boolean isFileExist(String filePath) {
	        if (ConvertUtils.isEmpty(filePath)) {
	            return false;
	        }

	        File file = new File(filePath);
	        return (file.exists() && file.isFile());
	    }

	    /**
	     * Indicates if this file represents a directory on the underlying file system.
	     * 
	     * @param directoryPath
	     * @return
	     */
	    public static boolean isFolderExist(String directoryPath) {
	        if (ConvertUtils.isEmpty(directoryPath)) {
	            return false;
	        }

	        File dire = new File(directoryPath);
	        return (dire.exists() && dire.isDirectory());
	    }

	    /**
	     * delete file or directory
	     * <ul>
	     * <li>if path is null or empty, return true</li>
	     * <li>if path not exist, return true</li>
	     * <li>if path exist, delete recursion. return true</li>
	     * <ul>
	     * 
	     * @param path
	     * @return
	     */
	    public static boolean deleteFile(String path) {
	        if (ConvertUtils.isEmpty(path)) {
	            return true;
	        }

	        File file = new File(path);
	        if (!file.exists()) {
	            return true;
	        }
	        if (file.isFile()) {
	            return file.delete();
	        }
	        if (!file.isDirectory()) {
	            return false;
	        }
	        for (File f : file.listFiles()) {
	            if (f.isFile()) {
	                f.delete();
	            } else if (f.isDirectory()) {
	                deleteFile(f.getAbsolutePath());
	            }
	        }
	        return file.delete();
	    }

	    /**
	     * get file size
	     * <ul>
	     * <li>if path is null or empty, return -1</li>
	     * <li>if path exist and it is a file, return file size, else return -1</li>
	     * <ul>
	     * 
	     * @param path
	     * @return returns the length of this file in bytes. returns -1 if the file does not exist.
	     */
	    public static long getFileSize(String path) {
	        if (ConvertUtils.isEmpty(path)) {
	            return -1;
	        }

	        File file = new File(path);
	        return (file.exists() && file.isFile() ? file.length() : -1);
	    }
	}
